/*! Copyright(c) 2008-2012
 *
 *\file		Stm8s_clk.c
 *\brief		
 *\details	
 *
 *\author	Cai Peifeng
 *\version	
 *\date		26May12
 *
 *\warning	
 *
 *\history \arg	26May12, Cai Peifeng, Create the file
 */

#include "Stm8s_clk.h"

CONST uint8_t HSIDivFactor[4] = {1, 2, 4, 8}; /*!< Holds the different HSI Divider factors */
CONST uint8_t CLKPrescTable[8] = {1, 2, 4, 8, 10, 16, 20, 40}; /*!< Holds the different CLK prescaler values */



/*!
 *\fn			void CLK_DeInit(void)
 *\brief   		When the CCOEN bit is set, the reset of the CCOR register require
  * two consecutive write instructions in order to reset first the CCOEN bit
  * and the second one is to reset the CCOSEL bits.
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_DeInit(void)
{
	CLK->ICKR = CLK_ICKR_RESET_VALUE;
	CLK->ECKR = CLK_ECKR_RESET_VALUE;
	CLK->SWR  = CLK_SWR_RESET_VALUE;
	CLK->SWCR = CLK_SWCR_RESET_VALUE;
	CLK->CKDIVR = CLK_CKDIVR_RESET_VALUE;
	CLK->PCKENR1 = CLK_PCKENR1_RESET_VALUE;
	CLK->PCKENR2 = CLK_PCKENR2_RESET_VALUE;
	CLK->CSSR = CLK_CSSR_RESET_VALUE;
	CLK->CCOR = CLK_CCOR_RESET_VALUE;
	while ((CLK->CCOR & CLK_CCOR_CCOEN)!= 0)
	{
		
	}

	CLK->CCOR = CLK_CCOR_RESET_VALUE;
	CLK->HSITRIMR = CLK_HSITRIMR_RESET_VALUE;
	CLK->SWIMCCR = CLK_SWCR_RESET_VALUE;
	
}


/*!
 *\fn			void CLK_FastHaltWakeUpCmd(FunctionalState NewState)
 *\brief   		Configures the High Speed Internal oscillator (HSI).
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_FastHaltWakeUpCmd(FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));

	if( DISABLE != NewState)
	{
		CLK->ICKR |= CLK_ICKR_FHWU;
	}
	else
	{
		CLK->ICKR &= (uint8_t)(~CLK_ICKR_FHWU);
	}
}


/*!
 *\fn			void CLK_HSECmd(FunctionalState NewState)
 *\brief   		Enable or Disable the External High Speed oscillator (HSE).
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_HSECmd(FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));

	if( DISABLE != NewState)
	{
		CLK->ECKR |= CLK_ECKR_HSEEN;
	}
	else
	{
		CLK->ECKR &= (uint8_t)(~CLK_ECKR_HSEEN);
	}
}


/*!
 *\fn			void CLK_HSICmd(FunctionalState NewState)
 *\brief   		Enables or disables the Internal High Speed oscillator (HSI).
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_HSICmd(FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));

	if( DISABLE != NewState)
	{
		CLK->ICKR |= CLK_ICKR_HSIEN;
	}
	else
	{
		CLK->ICKR &= (uint8_t)(~CLK_ICKR_HSIEN);
	}
}


/*!
 *\fn			void CLK_LSICmd(FunctionalState NewState)
 *\brief   		Enables or disables the Internal Low Speed oscillator (LSI).
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_LSICmd(FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));

	if( DISABLE != NewState)
	{
		CLK->ICKR |= CLK_ICKR_LSIEN;
	}
	else
	{
		CLK->ICKR &= (uint8_t)(~CLK_ICKR_LSIEN);
	}
}


/*!
 *\fn			void CLK_CCOCmd(FunctionalState NewState)
 *\brief   
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_CCOCmd(FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));

	if( DISABLE != NewState)
	{
		CLK->CCOR |= CLK_CCOR_CCOEN;
	}
	else
	{
		CLK->CCOR &= (uint8_t)(~CLK_CCOR_CCOEN);
	}
}


/*!
 *\fn			void CLK_ClockSwitchCmd(FunctionalState NewState)
 *\brief   		Starts or Stops manually the clock switch execution
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_ClockSwitchCmd(FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));

	if( DISABLE != NewState)
	{
		CLK->SWCR |= CLK_SWCR_SWEN;
	}
	else
	{
		CLK->SWCR &= (uint8_t)(~CLK_SWCR_SWEN);
	}
}


/*!
 *\fn			void CLK_SlowActiveHaltWakeUpCmd(FunctionalState NewState)
 *\brief   		Configures the slow active halt wake up
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_SlowActiveHaltWakeUpCmd(FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));

	if( DISABLE != NewState)
	{
		CLK->ICKR |= CLK_ICKR_SWUAH;
	}
	else
	{
		CLK->ICKR &= (uint8_t)(~CLK_ICKR_SWUAH);
	}
}


/*!
 *\fn			void CLK_PeripheralClockConfig(CLK_Peripheral_TypeDef CLK_Peripheral, FunctionalState NewState)
 *\brief   		Enables or disables the specified peripheral CLK
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_PeripheralClockConfig(CLK_Peripheral_TypeDef CLK_Peripheral, FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));
	assert_param(IS_CLK_PERIPHERAL_OK(CLK_Peripheral));

	if( ( (uint8_t)CLK_Peripheral & (uint8_t)0x10) == 0x00)
	{
		if( DISABLE != NewState)
		{
			CLK->PCKENR1 |= (uint8_t)( (uint8_t)1 << ( (uint8_t)CLK_Peripheral & (uint8_t)0x0F) );
		}
		else
		{
			CLK->PCKENR1 &= (uint8_t)(~(uint8_t)( ( (uint8_t)1 << ( (uint8_t)CLK_Peripheral & 
										(uint8_t)0x0F ) ) ) );
		}
	}
	else
	{
		if( DISABLE != NewState)		
		{
			CLK->PCKENR2 |= (uint8_t)( (uint8_t)1 << ( (uint8_t)CLK_Peripheral & (uint8_t)0x0F) );
		}
		else
		{
			CLK->PCKENR2 &= (uint8_t)(~(uint8_t)( ( (uint8_t)1 << ( (uint8_t)CLK_Peripheral & 
										(uint8_t)0x0F ) ) ) );
		}		
	}
}


/*!
 *\fn			ErrorStatus CLK_ClockSwitchConfig(CLK_SwitchMode_TypeDef CLK_SwitchMode, CLK_Source_TypeDef CLK_NewClock, FunctionalState ITState, CLK_CurrentClockState_TypeDef CLK_CurrentClockState)
 *\brief   		configures the Switch from one clock to another
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
ErrorStatus CLK_ClockSwitchConfig(CLK_SwitchMode_TypeDef CLK_SwitchMode, CLK_Source_TypeDef CLK_NewClock, FunctionalState ITState, CLK_CurrentClockState_TypeDef CLK_CurrentClockState)
{
	CLK_Source_TypeDef clock_master;
	uint16_t Downcounter = CLK_TIMEOUT;
	ErrorStatus result = ERROR;

	assert_param(IS_CLK_SOURCE_OK(CLK_NewClock));
	assert_param(IS_CLK_SWITCHMODE_OK(CLK_SwitchMode));
	assert_param(IS_FUNCTIONALSTATE_OK(ITState));
	assert_param(IS_CLK_CURRENTCLOCKSTATE_OK(CLK_CurrentClockState));

	clock_master = (CLK_Source_TypeDef)CLK->CMSR;

	if( CLK_SWITCHMODE_AUTO == CLK_SwitchMode)
	{
		CLK->SWCR |= CLK_SWCR_SWEN;

		if( DISABLE != ITState)
		{
			CLK->SWCR |= CLK_SWCR_SWIEN;
		}
		else
		{
			CLK->SWCR &= (uint8_t)(~CLK_SWCR_SWIEN);
		}


		CLK->SWR = (uint8_t)CLK_NewClock;

		while( ( ( ( CLK->SWCR & CLK_SWCR_SWBSY) != 0 ) && ( 0 != Downcounter) ))
		{
			Downcounter--;
		}

		if( 0 != Downcounter)
		{
			result = SUCCESS;
		}
		else
		{
			result = ERROR;
		}
		
	}
	else
	{
		if( DISABLE != ITState)
		{
			CLK->SWCR |= CLK_SWCR_SWIEN;
		}
		else
		{
			CLK->SWCR &= (uint8_t)(~CLK_SWCR_SWIEN);
		}


		CLK->SWR = (uint8_t)CLK_NewClock;

		result = SUCCESS;
	}

	if( (CLK_CURRENTCLOCKSTATE_DISABLE == CLK_CurrentClockState) && 
		( CLK_SOURCE_HSI == clock_master) )
	{
		CLK->ICKR &= (uint8_t)(~CLK_ICKR_HSIEN);
	}
	else if ( (CLK_CURRENTCLOCKSTATE_DISABLE == CLK_CurrentClockState) &&  
		( CLK_SOURCE_LSI == clock_master))
	{
		CLK->ICKR &= (uint8_t)(~CLK_ICKR_LSIEN);
	}
	else if ((CLK_CURRENTCLOCKSTATE_DISABLE == CLK_CurrentClockState) && 
		( CLK_SOURCE_HSE == clock_master))
	{
		CLK->ECKR &= (uint8_t)(~CLK_ECKR_HSEEN);
	}

	return result;
}


/*!
 *\fn			void CLK_HSIPrescalerConfig(CLK_Prescaler_TypeDef HSIPrescaler)
 *\brief   		Configures the HSI clock dividers.
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_HSIPrescalerConfig(CLK_Prescaler_TypeDef HSIPrescaler)
{
	assert_param(IS_CLK_HSIPRESCALER_OK(HSIPrescaler));

	CLK->CKDIVR &= (uint8_t)(~CLK_CKDIVR_HSIDIV);

	CLK->CKDIVR |= (uint8_t)HSIPrescaler;
}


/*!
 *\fn			void CLK_CCOConfig(CLK_Output_TypeDef CLK_CCO)
 *\brief   		Output the selected clock on a dedicated I/O pin
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_CCOConfig(CLK_Output_TypeDef CLK_CCO)
{
	assert_param(IS_CLK_OUTPUT_OK(CLK_CCO));

	CLK->CCOR &= (uint8_t)(~CLK_CCOR_CCOSEL);

	CLK->CCOR |= (uint8_t)CLK_CCO;

	CLK->CCOR |= CLK_CCOR_CCOEN;
}


/*!
 *\fn			void CLK_ITConfig(CLK_IT_TypeDef CLK_IT, FunctionalState NewState)
 *\brief   		Enables or disables the specified CLK interrupts
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_ITConfig(CLK_IT_TypeDef CLK_IT, FunctionalState NewState)
{
	assert_param(IS_FUNCTIONALSTATE_OK(NewState));
	assert_param(IS_CLK_IT_OK(CLK_IT));

	if( DISABLE != NewState)
	{
		switch(CLK_IT)
		{
		case CLK_IT_SWIF:
			CLK->SWCR |= CLK_SWCR_SWIEN;
			break;
		case CLK_IT_CSSD:
			CLK->CSSR |= CLK_CSSR_CSSDIE;
			break;
		default:
			break;
		}
	}
	else
	{
		switch(CLK_IT)
		{
		case CLK_IT_SWIF:
			CLK->SWCR &= (uint8_t)(~CLK_SWCR_SWIEN);
			break;
		case CLK_IT_CSSD:
			CLK->CSSR &= (uint8_t)(~CLK_CSSR_CSSDIE);
			break;
		default:
			break;			
		}
	}
}

/*!
 *\fn			void CLK_SYSCLKConfig(CLK_Prescaler_TypeDef CLK_Prescaler)
 *\brief   		Configures the HSI and CPU clock dividers
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_SYSCLKConfig(CLK_Prescaler_TypeDef CLK_Prescaler)
{
    assert_param(IS_CLK_PRESCALER_OK(CLK_Prescaler));

	if( ( (uint8_t)CLK_Prescaler & (uint8_t)0x80) == 0x00 )
	{
		CLK->CKDIVR &= (uint8_t)(~CLK_CKDIVR_HSIDIV);
		CLK->CKDIVR |= (uint8_t)((uint8_t)CLK_Prescaler & (uint8_t)CLK_CKDIVR_HSIDIV);
	}
	else
	{
        CLK->CKDIVR &= (uint8_t)(~CLK_CKDIVR_CPUDIV);
        CLK->CKDIVR |= (uint8_t)((uint8_t)CLK_Prescaler & (uint8_t)CLK_CKDIVR_CPUDIV);		
	}
}

/*!
 *\fn			void CLK_SWIMConfig(CLK_SWIMDivider_TypeDef CLK_SWIMDivider)
 *\brief   		Configures the SWIM clock frequency on the fly
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_SWIMConfig(CLK_SWIMDivider_TypeDef CLK_SWIMDivider)
{
    assert_param(IS_CLK_SWIMDIVIDER_OK(CLK_SWIMDivider));

	if( CLK_SWIMDIVIDER_2 != CLK_SWIMDivider)
	{
		CLK->SWIMCCR |= CLK_SWIMCCR_SWIMDIV;
	}
	else
	{
		CLK->SWIMCCR &= (uint8_t)(~CLK_SWIMCCR_SWIMDIV);
	}
}

/*!
 *\fn			void CLK_ClockSecuritySystemEnable(void)
 *\brief   		Enables the Clock Security System.
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_ClockSecuritySystemEnable(void)
{
    CLK->CSSR |= CLK_CSSR_CSSEN;
}


/*!
 *\fn			CLK_Source_TypeDef CLK_GetSYSCLKSource(void)
 *\brief   		Returns the clock source used as system clock.
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
CLK_Source_TypeDef CLK_GetSYSCLKSource(void)
{
    return((CLK_Source_TypeDef)CLK->CMSR);
	
}

/*!
 *\fn			uint32_t CLK_GetClockFreq(void)
 *\brief   		This function returns the frequencies of different on chip clocks
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
uint32_t CLK_GetClockFreq(void)
{
    uint32_t clockfrequency = 0;
    CLK_Source_TypeDef clocksource = CLK_SOURCE_HSI;
    uint8_t tmp = 0, presc = 0;

    /* Get CLK source. */
    clocksource = (CLK_Source_TypeDef)CLK->CMSR;

    if (clocksource == CLK_SOURCE_HSI)
    {
        tmp = (uint8_t)(CLK->CKDIVR & CLK_CKDIVR_HSIDIV);
        tmp = (uint8_t)(tmp >> 3);
        presc = HSIDivFactor[tmp];
        clockfrequency = HSI_VALUE / presc;
    }
    else if ( clocksource == CLK_SOURCE_LSI)
    {
        clockfrequency = LSI_VALUE;
    }
    else
    {
        clockfrequency = HSE_VALUE;
    }

    return((uint32_t)clockfrequency);	
}


/*!
 *\fn			void CLK_AdjustHSICalibrationValue(CLK_HSITrimValue_TypeDef CLK_HSICalibrationValue)
 *\brief   		Adjusts the Internal High Speed oscillator (HSI) calibration value
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_AdjustHSICalibrationValue(CLK_HSITrimValue_TypeDef CLK_HSICalibrationValue)
{	
    assert_param(IS_CLK_HSITRIMVALUE_OK(CLK_HSICalibrationValue));

    CLK->HSITRIMR = (uint8_t)( (uint8_t)(CLK->HSITRIMR & (uint8_t)(~CLK_HSITRIMR_HSITRIM))|((uint8_t)CLK_HSICalibrationValue));
	
}


/*!
 *\fn			void CLK_SYSCLKEmergencyClear(void)
 *\brief   		Reset the SWBSY flag (SWICR Reister)
 *This function reset SWBSY flag in order to reset clock switch operations (target
 * oscillator is broken, stabilization is longing too much, etc.).  If at the same time \n
 * software attempts to set SWEN and clear SWBSY, SWBSY action takes precedence 
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_SYSCLKEmergencyClear(void)
{
    CLK->SWCR &= (uint8_t)(~CLK_SWCR_SWBSY);
}


/*!
 *\fn			FlagStatus CLK_GetFlagStatus(CLK_Flag_TypeDef CLK_FLAG)
 *\brief   		Checks whether the specified CLK flag is set or not
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
FlagStatus CLK_GetFlagStatus(CLK_Flag_TypeDef CLK_FLAG)
{
    uint16_t statusreg = 0;
    uint8_t tmpreg = 0;
    FlagStatus bitstatus = RESET;

    assert_param(IS_CLK_FLAG_OK(CLK_FLAG));

    statusreg = (uint16_t)((uint16_t)CLK_FLAG & (uint16_t)0xFF00);


    if (statusreg == 0x0100)
    {
        tmpreg = CLK->ICKR;
    }
    else if (statusreg == 0x0200)
    {
        tmpreg = CLK->ECKR;
    }
    else if (statusreg == 0x0300)
    {
        tmpreg = CLK->SWCR;
    }
    else if (statusreg == 0x0400)
    {
        tmpreg = CLK->CSSR;
    }
    else
    {
        tmpreg = CLK->CCOR;
    }

    if ((tmpreg & (uint8_t)CLK_FLAG) != (uint8_t)RESET)
    {
        bitstatus = SET;
    }
    else
    {
        bitstatus = RESET;
    }

    /* Return the flag status */
    return((FlagStatus)bitstatus);	
}


/*!
 *\fn			ITStatus CLK_GetITStatus(CLK_IT_TypeDef CLK_IT)
 *\brief   		Checks whether the specified CLK interrupt has is enabled or not.
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
ITStatus CLK_GetITStatus(CLK_IT_TypeDef CLK_IT)
{
    ITStatus bitstatus = RESET;

    assert_param(IS_CLK_IT_OK(CLK_IT));

    if (CLK_IT == CLK_IT_SWIF)
    {
        if ((CLK->SWCR & (uint8_t)CLK_IT) == (uint8_t)0x0C)
        {
            bitstatus = SET;
        }
        else
        {
            bitstatus = RESET;
        }
    }
    else
    {
        if ((CLK->CSSR & (uint8_t)CLK_IT) == (uint8_t)0x0C)
        {
            bitstatus = SET;
        }
        else
        {
            bitstatus = RESET;
        }
    }

    /* Return the CLK_IT status */
    return bitstatus;	
}


/*!
 *\fn			void CLK_ClearITPendingBit(CLK_IT_TypeDef CLK_IT)
 *\brief   		Clears the CLK interrupt pending bits
 *
 *\param[in]   
 *\param[out]  
 *
 *\return  
 *\retval  
 *
 *\note		  
 */
void CLK_ClearITPendingBit(CLK_IT_TypeDef CLK_IT)
{
    assert_param(IS_CLK_IT_OK(CLK_IT));

    if (CLK_IT == (uint8_t)CLK_IT_CSSD)
    {
        CLK->CSSR &= (uint8_t)(~CLK_CSSR_CSSD);
    }
    else
    {
        CLK->SWCR &= (uint8_t)(~CLK_SWCR_SWIF);
    }	
}
